package com.xizi.lotterySystem.service.Impl;


import com.xizi.lotterySystem.common.exception.ControllerException;
import com.xizi.lotterySystem.common.exception.ServiceException;
import com.xizi.lotterySystem.common.exception.errorCode.ServiceErrorCodeConstants;
import com.xizi.lotterySystem.controller.param.ActivityPrizeList;
import com.xizi.lotterySystem.controller.param.ActivityUserList;
import com.xizi.lotterySystem.controller.param.CreateActivityParam;
import com.xizi.lotterySystem.controller.param.PageListParam;
import com.xizi.lotterySystem.dao.dataobject.ActivityDO;
import com.xizi.lotterySystem.dao.dataobject.ActivityPrizeDO;
import com.xizi.lotterySystem.dao.dataobject.ActivityUserDO;
import com.xizi.lotterySystem.dao.dataobject.PrizeDO;
import com.xizi.lotterySystem.dao.mapper.ActivityMapper;
import com.xizi.lotterySystem.dao.mapper.ActivityPrizeMapper;
import com.xizi.lotterySystem.dao.mapper.ActivityUserMapper;
import com.xizi.lotterySystem.dao.mapper.PrizeMapper;
import com.xizi.lotterySystem.service.IActivityService;
import com.xizi.lotterySystem.service.dto.*;
import com.xizi.lotterySystem.service.enumerate.ActivityPrizeStatusEnum;
import com.xizi.lotterySystem.service.enumerate.ActivityPrizeTiersEnum;
import com.xizi.lotterySystem.service.enumerate.ActivityStatusEnum;
import com.xizi.lotterySystem.service.enumerate.ActivityUserStatusEnum;
import com.xizi.lotterySystem.utils.JacksonUtil;
import com.xizi.lotterySystem.utils.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements IActivityService {

    private static final Logger log = LoggerFactory.getLogger(ActivityServiceImpl.class);

    private final String ACTIVITY_PREFIX = "Activity";

    private final Long ACTIVITY_EXPIRATION_TIME = 7 * 24 * 60 * 60L;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Override
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //1. 参数校验
        checkCreateActivityParam(param);

        //2. 创建活动
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING);
        int row1 = activityMapper.insert(activityDO);
        if(row1 != 1){
            throw new ServiceException(ServiceErrorCodeConstants.SQL_ERROR);
        }

        //3. 活动奖品添加
        //  收集并去重
        List<ActivityPrizeList> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> aPDO = prizeParams.stream()
                .collect(Collectors.toMap(
                        // key：按 activityId + prizeId 组合
                        p -> p.getPrizeId() + "_" + activityDO.getId(),
                        // value：转成 DO
                        p -> {
                            ActivityPrizeDO ap = new ActivityPrizeDO();
                            ap.setActivityId(activityDO.getId());
                            ap.setPrizeId(p.getPrizeId());
                            ap.setPrizeAmount(p.getPrizeAmount());
                            ap.setPrizeTiers(ActivityPrizeTiersEnum.forTiers(p.getPrizeTiers()));
                            ap.setStatus(ActivityPrizeStatusEnum.INIT.name());
                            return ap;
                        },
                        // 如果 key 冲突，保留第一条
                        (oldVal, newVal) -> oldVal
                ))
                .values()
                .stream()
                .collect(Collectors.toList());

        //4. 活动参与者添加
        List<ActivityUserList> activityUserDOList = param.getActivityUserList();
        List<ActivityUserDO> aUDO = activityUserDOList
                .stream()
                .collect(Collectors.toMap(
                        // key：按 activityId + prizeId 组合
                        u -> u.getUserId() + "_" + activityDO.getId(),
                        // value：转成 DO
                        u -> {
                            ActivityUserDO ap = new ActivityUserDO();
                            ap.setUserId(u.getUserId());
                            ap.setActivityId(activityDO.getId());
                            ap.setStatus(ActivityUserStatusEnum.INIT.name());
                            ap.setUserName(u.getUserName());
                            return ap;
                        },
                        // 如果 key 冲突，保留第一条
                        (oldVal, newVal) -> oldVal
                ))
                .values()
                .stream()
                .collect(Collectors.toList());

        // 参与抽奖的人数  >=  奖品数量
        long userCount = aUDO.size();
        long prizeCount = aPDO.stream()
                .mapToLong(ActivityPrizeDO::getPrizeAmount)
                .sum();
        if(prizeCount > userCount){
            log.error(ServiceErrorCodeConstants.CREATE_ACTIVITY_USER_PRIZE_COUNT_ERROR.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_USER_PRIZE_COUNT_ERROR);
        }

        //  批量插入
        int APRow =  activityPrizeMapper.batchInsert(aPDO);
        int AURow =activityUserMapper.batchInsert(aUDO);

        if(APRow != aPDO.size() || AURow != aUDO.size()){
            throw new ServiceException(ServiceErrorCodeConstants.SQL_ERROR);
        }
        List<Long> ids = aPDO.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDO = prizeMapper.selectPrizeByIds(ids);

        //5. 信息整合
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO,aUDO,pDO,aPDO);

        //6. Redis 存储信息
        cacheActivity(activityDetailDTO);

        //7. 返回结果
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setId(activityDO.getId());
        return createActivityDTO;
    }



    private void cacheActivity(ActivityDetailDTO activityDetailDTO){
        if (activityDetailDTO == null){
            log.error("创建的活动信息存储 redis 失败，activityDetailDTO 为 null !");
            return;
        }
        try {
            RedisUtil.set(ACTIVITY_PREFIX +  activityDetailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(activityDetailDTO),
                    ACTIVITY_EXPIRATION_TIME);
        }catch (Exception e){
            log.warn("创建的活动信息存储 redis 失败！e{}",e.getMessage());
        }
    }

    private ActivityDetailDTO getActivityDetailDTOByCache(Long activityId){
        if (activityId == null || activityId < 0){
            log.warn("findDetail : {}", ServiceErrorCodeConstants.PARAM_ERROR);
            throw new ControllerException(ServiceErrorCodeConstants.PARAM_ERROR);
        }
        //1. 先从 redis 中取
        try {
            ActivityDetailDTO activityDetailDTO = JacksonUtil.readValue(RedisUtil.get(ACTIVITY_PREFIX + activityId), ActivityDetailDTO.class);
            return activityDetailDTO;
        }catch (Exception e){
            log.warn("Redis 中获取活动详情失败！ findDetail : {}  {}",
                    e.getMessage(),ACTIVITY_PREFIX + activityId);
        }
        return null;
    }


    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityUserDO> activityUserDOList,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList) {
        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDesc(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.fromName(activityDO.getStatus().name()));

        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList
                .stream()
                .map(apDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(apDO.getPrizeId());
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList
                            .stream()
                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    // 如果PrizeDO为空，不执行当前方法，不为空才执行
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forTiers(apDO.getPrizeTiers().name()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());

        detailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream()
                .map(auDO -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(auDO.getUserId());
                    userDTO.setUserName(auDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());
        detailDTO.setUserDTOList(userDTOList);
        return detailDTO;
    }




    private void checkCreateActivityParam(CreateActivityParam param) {
        // 校验空值
        if(param == null || !StringUtils.hasLength(param.getActivityName())
                || !StringUtils.hasLength(param.getDescription())){
            log.error("createActivity () 方法 ，参数校验失败！ 存在空值！");
            throw new ServiceException(ServiceErrorCodeConstants.PARAM_ERROR);
        }
        if(param.getActivityPrizeList() == null || param.getActivityUserList() == null){
            log.error("createActivity () 方法 ，参数校验失败！ 存在空值！");
            throw new ServiceException(ServiceErrorCodeConstants.PARAM_ERROR);
        }

        // 校验用户信息是否存在于数据库
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(ActivityUserList::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existUserIds = activityUserMapper.selectUserIds(userIds);

        userIds.forEach(id ->{
            if(!existUserIds.contains(id)){
                log.error(ServiceErrorCodeConstants.CREATE_ACTIVITY_USER_INFORMATION_ERROR.getMessage());
                throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_USER_INFORMATION_ERROR);
            }
        });
        if(userIds.size() != existUserIds.size()){
            log.error(ServiceErrorCodeConstants.CREATE_ACTIVITY_USER_INFORMATION_ERROR.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_USER_INFORMATION_ERROR);
        }
        // 校验奖品信息是否存在于数据库中
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(ActivityPrizeList::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPrizeIds = activityPrizeMapper.selectPrizeIds(prizeIds);
        prizeIds.forEach(id ->{
            if(!existPrizeIds.contains(id)){
                log.error(ServiceErrorCodeConstants.CREATE_ACTIVITY_PRIZE_INFORMATION_ERROR.getMessage());
                throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_PRIZE_INFORMATION_ERROR);
            }
        });

        if(prizeIds.size() != existPrizeIds.size()){
            log.error(ServiceErrorCodeConstants.CREATE_ACTIVITY_PRIZE_INFORMATION_ERROR.getMessage());
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_PRIZE_INFORMATION_ERROR);
        }

        log.info("校验创建活动参数通过！");
    }


    @Override
    public Long selectCount() {
        Long count = activityMapper.selectCount();
        return count;
    }

    @Override
    public List<ActivityDO> selectList(PageListParam param) {
        List<ActivityDO> list = activityMapper.selectList(param.getPageSize(),param.getOffset());
        return list == null ? new ArrayList<>() : list;
    }

    @Override
    public ActivityDetailDTO selectDetail(Long activityId) {
        if (activityId == null || activityId < 0){
            log.warn("findDetail : {}", ServiceErrorCodeConstants.PARAM_ERROR);
            throw new ControllerException(ServiceErrorCodeConstants.PARAM_ERROR);
        }
        //1. 先从 redis 中取
        ActivityDetailDTO activityDetailDTO = getActivityDetailDTOByCache(activityId);
        if(activityDetailDTO != null){
            return activityDetailDTO;
        }

        //2. 从 数据库中取
        ActivityDO activityDO = activityMapper.selectById(activityId);

        if(activityDO == null){
            log.warn(ServiceErrorCodeConstants.ACTIVITY_ID_ERROR.getMessage() + "activityId: " + activityId);
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_ERROR);
        }

        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectByActivityId(activityId);
        List<Long> ids = activityPrizeDOList
                .stream()
                .map(ActivityPrizeDO::getPrizeId)
                .toList();

        List<PrizeDO> prizeDOList = prizeMapper.selectPrizeByIds(ids);

        activityDetailDTO = convertToActivityDetailDTO(activityDO, activityUserDOList, prizeDOList, activityPrizeDOList);

        //3.存储到 redis 中
        cacheActivity(activityDetailDTO);

        return activityDetailDTO;
    }

    @Override
    public void updateCacheActivityDetail(Long activityId) {
        if (activityId == null || activityId < 0){
            log.warn("findDetail : {}", ServiceErrorCodeConstants.PARAM_ERROR);
            throw new ControllerException(ServiceErrorCodeConstants.PARAM_ERROR);
        }

        //从 数据库中取
        ActivityDO activityDO = activityMapper.selectById(activityId);

        if(activityDO == null){
            log.warn(ServiceErrorCodeConstants.ACTIVITY_ID_ERROR.getMessage() + "activityId: " + activityId);
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_ERROR);
        }

        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectByActivityId(activityId);
        List<Long> ids = activityPrizeDOList
                .stream()
                .map(ActivityPrizeDO::getPrizeId)
                .toList();

        List<PrizeDO> prizeDOList = prizeMapper.selectPrizeByIds(ids);

        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO, activityUserDOList, prizeDOList, activityPrizeDOList);

        //3.存储到 redis 中
        cacheActivity(activityDetailDTO);
    }


}
